package com.xxx.mch.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.xxx.mch.config.MchInterepter;
import com.xxx.mch.dao.DeterminationScoreMapper;
import com.xxx.mch.dao.DeterminationScoreTemplateMapper;
import com.xxx.mch.dao.TotalScoreMapper;
import com.xxx.mch.domain.dto.DeterminationScoreDetailDto;
import com.xxx.mch.domain.dto.DeterminationScoreItemDto;
import com.xxx.mch.domain.dto.DeterminationScoreTemplateDto;
import com.xxx.mch.domain.dto.ScoreItem;
import com.xxx.mch.domain.po.*;
import com.xxx.mch.domain.request.DeterminationQueryScoreRequest;
import com.xxx.mch.domain.request.DeterminationScoreTemplateRequest;
import com.xxx.mch.domain.request.QueryScoreRequest;
import com.xxx.mch.domain.request.RuleSearchRequest;
import com.xxx.mch.exception.BusinessException;
import com.xxx.mch.service.DeterminationScoreService;
import com.xxx.mch.service.SystemConfigService;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import javax.annotation.Resource;

import com.xxx.mch.utils.Wrapper;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import tk.mybatis.mapper.weekend.Weekend;
import tk.mybatis.mapper.weekend.WeekendCriteria;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author FengHaixin
 * @description 一句话描述该文件的用途
 * @date 2020/2/26
 */
@Service
public class DeterminationScoreServiceImpl implements DeterminationScoreService
{

    private static final String DATE_FORMAT = "yyyy-MM";

    @Value ("${hive.login-name-url}")
    private String loginNameUrl;

    @Value ("${hive.site-code}")
    private String siteCode;

    @Autowired
    private ApplicationContext applicationContext;

    @Resource
    private SystemConfigService configService;

    @Autowired
    private DeterminationScoreMapper determinationScoreMapper;

    @Autowired
    private TotalScoreMapper totalScoreMapper;

    @Autowired
    private DeterminationScoreTemplateMapper determinationScoreTemplateMapper;

    @Autowired
    private DeterminationScoreServiceImpl determinationScoreService;


    @Override
    @Transactional (rollbackFor = Exception.class)
    public void saveScore(List<DeterminationScore> determinationScoreList, String month)
    {
        if (CollectionUtils.isEmpty(determinationScoreList))
        {
            throw new BusinessException("参数错误!");
        }

        determinationScoreList.stream().map(value -> saveOrUpdate(value, month)).reduce(BigDecimal::add).ifPresent(totalScore -> {
            String uid = determinationScoreList.get(0).getUid();
            TotalScore toDbScore = new TotalScore();
            toDbScore.setDeterminationScore(totalScore.multiply(this.getWeight()));
            toDbScore.setUid(uid);
            toDbScore.setMonth(month);
            applicationContext.publishEvent(toDbScore);
        });
    }


    private BigDecimal getWeight()
    {
        Integer role = MchInterepter.UID.get().getRole();
        Map<String, SystemConfig> leaderWeight = configService.queryConfigByMenu("leaderWeight");
        //FIXME 需要和用户的role关联起来
        SystemConfig leaderScoreWeight = leaderWeight.get(String.valueOf(role));
        if (Objects.isNull(leaderScoreWeight))
        {
            return BigDecimal.ONE;
        }
        return leaderScoreWeight.getValue();
    }


    private BigDecimal saveOrUpdate(DeterminationScore determinationScore, String month)
    {
        DeterminationScoreTemplate template = determinationScoreTemplateMapper.selectByPrimaryKey(determinationScore.getRuleId());
        if (Objects.isNull(template))
        {
            throw new BusinessException("细则不存在: ruleId = " + determinationScore.getRuleId());
        }
        determinationScore.setMonth(month);
        determinationScore.setScore(template.getScore());
        Weekend<DeterminationScore> weekend = new Weekend<>(DeterminationScore.class);
        WeekendCriteria<DeterminationScore, Object> criteria = weekend.weekendCriteria();
        criteria.andEqualTo(DeterminationScore::getUid, determinationScore.getUid());
        criteria.andEqualTo(DeterminationScore::getRole, determinationScore.getRole());
        criteria.andEqualTo(DeterminationScore::getEvaluateId, determinationScore.getEvaluateId());
        criteria.andEqualTo(DeterminationScore::getMonth, determinationScore.getMonth());
        DeterminationScore oldData = determinationScoreMapper.selectOneByExample(weekend);
        if (Objects.nonNull(oldData))
        {
            determinationScore.setId(oldData.getId());
            determinationScoreMapper.updateByPrimaryKeySelective(determinationScore);
            if (Objects.nonNull(oldData.getScore()))
            {
                return determinationScore.getScore().subtract(oldData.getScore());
            }
        }
        else
        {
            determinationScoreMapper.insertSelective(determinationScore);
        }
        return determinationScore.getScore();
    }


    @Override
    public BigDecimal querySumScore(DeterminationQueryScoreRequest queryScoreRequest)
    {
        return determinationScoreMapper.querySumScore(queryScoreRequest);
    }


    @Transactional (rollbackFor = Exception.class)
    @Override
    public PageInfo<DeterminationScore> queryScoreRecordList(DeterminationQueryScoreRequest queryScoreRequest)
    {
        queryScoreRequest.setPageSize(1000);
        Page<DeterminationScore> page = PageHelper.startPage(queryScoreRequest.getPage(), queryScoreRequest.getPageSize());
        PageInfo<DeterminationScore> resultList = page.doSelectPageInfo(() -> {
            determinationScoreMapper.queryGroupByEvaluateId(queryScoreRequest);
        });
        List<DeterminationScoreTemplateDto> templateDtoList = determinationScoreService.getTaskTemplate(null, 1);
        templateDtoList.forEach(templateDto -> {
            if (resultList.getList().stream().noneMatch(
                    determinationScore -> determinationScore.getEvaluateId().equals(templateDto.getId())))
            {
                DeterminationScoreTemplate rule = determinationScoreTemplateMapper.selectOne(
                        new DeterminationScoreTemplate().setParentId(templateDto.getId()).setSelected(true));

                DeterminationScore score = new DeterminationScore();
                score.setUid(queryScoreRequest.getUid());
                score.setEvaluateId(templateDto.getId());
                score.setEvaluateName(templateDto.getName());
                if (Objects.nonNull(rule))
                {
                    score.setScore(rule.getScore());
                    score.setRuleId(rule.getId());
                    score.setRuleName(rule.getName());
                }
                resultList.getList().add(score);
            }
        });
        return resultList;
    }


    @Override
    public DeterminationScoreDetailDto queryScoreRecord(DeterminationQueryScoreRequest queryScoreRequest)
    {

        //获取模板
        List<DeterminationScoreTemplateDto> templateDtoList = determinationScoreService.getTaskTemplate(null, 1);

        //根据用户角色查询
        DeterminationScoreDetailDto detailDto = new DeterminationScoreDetailDto();

        //TODO 连接数据库查询用户角色
        Integer role = 5;
        detailDto.setRole(role);
        List<DeterminationScore> scoreList = determinationScoreMapper.queryByUid(queryScoreRequest);
        //先role，再内容id
        Map<Integer, Map<Integer, ScoreItem>> scoreMap = scoreList.stream().collect(Collectors.groupingBy(DeterminationScore::getRole,
                                                                                                          Collectors.toMap(
                                                                                                                  DeterminationScore::getEvaluateId,
                                                                                                                  this::getScoreItem)));
        List<DeterminationScoreItemDto> scoreItemDtoList = templateDtoList.stream().map(templateDto -> {
            DeterminationScoreItemDto itemDto = new DeterminationScoreItemDto();
            itemDto.setContent(templateDto.getName());
            itemDto.setFenguan(scoreMap.getOrDefault(1, new HashMap<>()).getOrDefault(templateDto.getId(), new ScoreItem()));
            itemDto.setLunzhi(scoreMap.getOrDefault(2, new HashMap<>()).getOrDefault(templateDto.getId(), new ScoreItem()));
            itemDto.setDangwei(scoreMap.getOrDefault(3, new HashMap<>()).getOrDefault(templateDto.getId(), new ScoreItem()));
            itemDto.setZhuyao(scoreMap.getOrDefault(4, new HashMap<>()).getOrDefault(templateDto.getId(), new ScoreItem()));
            itemDto.setNormal(scoreMap.getOrDefault(5, new HashMap<>()).getOrDefault(templateDto.getId(), new ScoreItem()));
            return itemDto;
        }).collect(Collectors.toList());

        scoreItemDtoList.stream().reduce((prev, next) -> {
            DeterminationScoreItemDto itemDto = new DeterminationScoreItemDto();
            itemDto.setContent("得分");
            itemDto.setFenguan(this.add(prev.getFenguan(), next.getFenguan()));
            itemDto.setLunzhi(this.add(prev.getLunzhi(), next.getLunzhi()));
            itemDto.setDangwei(this.add(prev.getDangwei(), next.getDangwei()));
            itemDto.setZhuyao(this.add(prev.getZhuyao(), next.getZhuyao()));
            itemDto.setNormal(this.add(prev.getNormal(), next.getNormal()));
            return itemDto;
        }).ifPresent(scoreItemDtoList::add);
        detailDto.setScoreList(scoreItemDtoList);
        return detailDto;
    }


    @Override
    public void createOrUpdateTemplate(List<DeterminationScoreTemplateRequest> templateRequestList)
    {
        if (CollectionUtils.isEmpty(templateRequestList))
        {
            return;
        }

        templateRequestList.forEach(this::doCreateOrUpdateTemplate);
    }


    @Override
    public void deleteTemplate(Integer templateId)
    {
        List<DeterminationScoreTemplate> templateList = determinationScoreTemplateMapper.select(
                new DeterminationScoreTemplate().setParentId(templateId));
        if (!CollectionUtils.isEmpty(templateList))
        {
            throw new BusinessException("该菜单下还有子菜单，请先删除子菜单");
        }
        determinationScoreTemplateMapper.deleteByPrimaryKey(templateId);
    }


    @Override
    public List<DeterminationScoreTemplateDto> getTaskTemplate(Integer parentId, Integer depth)
    {
        List<DeterminationScoreTemplate> templates = determinationScoreTemplateMapper.selectRoot(parentId);
        List<DeterminationScoreTemplateDto> taskScoreTemplateDtoList = this.convert(templates);
        this.querySubContext(taskScoreTemplateDtoList, --depth);
        return taskScoreTemplateDtoList;
    }


    @Override
    public List<DeterminationScoreTemplateDto> ruleSearch(RuleSearchRequest ruleSearchRequest)
    {
        Weekend<DeterminationScoreTemplate> weekend = new Weekend<>(DeterminationScoreTemplate.class);
        WeekendCriteria<DeterminationScoreTemplate, Object> criteria = weekend.weekendCriteria();
        criteria.andEqualTo(DeterminationScoreTemplate::getParentId, ruleSearchRequest.getParentId());
        criteria.andLike(DeterminationScoreTemplate::getName, "%" + ruleSearchRequest.getName() + "%");
        return this.convert(determinationScoreTemplateMapper.selectByExample(weekend));
    }


    private void querySubContext(List<DeterminationScoreTemplateDto> templates, Integer depth)
    {
        //终止条件
        if (CollectionUtils.isEmpty(templates) || depth <= 0)
        {
            return;
        }

        for (DeterminationScoreTemplateDto determinationScoreTemplateDto : templates)
        {
            List<DeterminationScoreTemplate> subContextList = determinationScoreTemplateMapper.select(
                    new DeterminationScoreTemplate().setParentId(determinationScoreTemplateDto.getId()));
            //设置为下一级
            List<DeterminationScoreTemplateDto> taskScoreTemplateDtoList = this.convert(subContextList);
            determinationScoreTemplateDto.setChildren(taskScoreTemplateDtoList);
            this.querySubContext(taskScoreTemplateDtoList, --depth);
            ++depth;
        }
    }


    private List<DeterminationScoreTemplateDto> convert(List<DeterminationScoreTemplate> templates)
    {
        return templates.stream().map(qualityScoreTemplate -> {
            DeterminationScoreTemplateDto templateDto = new DeterminationScoreTemplateDto();
            BeanUtils.copyProperties(qualityScoreTemplate, templateDto);
            return templateDto;
        }).collect(Collectors.toList());
    }


    private void doCreateOrUpdateTemplate(DeterminationScoreTemplateRequest templateRequest)
    {
        DeterminationScoreTemplate template = new DeterminationScoreTemplate();
        BeanUtils.copyProperties(templateRequest, template);

        if (this.isCreate(templateRequest))
        {
            this.doCreate(template);
        }
        else
        {
            this.doUpdate(template);
        }

        List<DeterminationScoreTemplateRequest> subContext = templateRequest.getChildren();
        if (CollectionUtils.isEmpty(subContext))
        {
            return;
        }
        subContext.forEach(taskScoreTemplate -> {
            taskScoreTemplate.setParentId(template.getId());
            this.doCreateOrUpdateTemplate(taskScoreTemplate);
        });

    }


    private void doUpdate(DeterminationScoreTemplate template)
    {
        //template.setType();
        determinationScoreTemplateMapper.updateByPrimaryKeySelective(template);

    }


    private void doCreate(DeterminationScoreTemplate template)
    {
        template.setStatus(true);
        determinationScoreTemplateMapper.insertSelective(template);
    }


    private boolean isCreate(DeterminationScoreTemplateRequest templateRequest)
    {
        return Objects.isNull(templateRequest.getId());
    }


    public List<DeterminationScore> getRules(Integer evaluateId, QueryScoreRequest queryScoreRequest)
    {
        Weekend<DeterminationScore> weekend = new Weekend<>(DeterminationScore.class);
        WeekendCriteria<DeterminationScore, Object> criteria = weekend.weekendCriteria();
        criteria.andEqualTo(DeterminationScore::getUid, queryScoreRequest.getUid());
        criteria.andEqualTo(DeterminationScore::getEvaluateId, evaluateId);
        criteria.andCondition(
                "DATE_FORMAT(STR_TO_DATE(create_time,\"%Y-%m-%d %H:%i:%s\"), '%Y-%m')= '" + queryScoreRequest.getDate() + "'");
        return determinationScoreMapper.selectByExample(weekend);
    }


    public List<DeterminationScoreTemplate> getTemplateRules(Integer evaluateId)
    {
        Weekend<DeterminationScoreTemplate> weekend = new Weekend<>(DeterminationScoreTemplate.class);
        WeekendCriteria<DeterminationScoreTemplate, Object> criteria = weekend.weekendCriteria();
        criteria.andEqualTo(DeterminationScoreTemplate::getParentId, evaluateId);
        return determinationScoreTemplateMapper.selectByExample(weekend);
    }


    private ScoreItem getScoreItem(DeterminationScore score)
    {
        ScoreItem scoreItem = new ScoreItem();
        scoreItem.setRuleId(score.getRuleId());
        scoreItem.setRuleName(score.getRuleName());
        scoreItem.setScore(score.getScore());
        return scoreItem;
    }


    private ScoreItem add(ScoreItem prev, ScoreItem next)
    {
        ScoreItem item = new ScoreItem();
        item.setScore(this.getScore(prev).add(this.getScore(next)));
        return item;
    }


    private BigDecimal getScore(ScoreItem item)
    {
        if (Objects.isNull(item))
        {
            return BigDecimal.ZERO;
        }

        return Objects.isNull(item.getScore()) ? BigDecimal.ZERO : item.getScore();
    }

    public void updateDeterminationLock(List<String> uidList, String month, Integer status)
    {
        Weekend<TotalScore> weekend = new Weekend<>(TotalScore.class);
        WeekendCriteria<TotalScore, Object> criteria = weekend.weekendCriteria();
        criteria.andEqualTo(TotalScore::getMonth, month);
        criteria.andIn(TotalScore::getUid, uidList);
        totalScoreMapper.updateByExampleSelective(new TotalScore(true).setLockStatus(status), weekend);
    }

}
